package com.dj.mingkong.admin.biz.service.music.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dj.mingkong.admin.biz.constant.Constants;
import com.dj.mingkong.admin.biz.controller.music.vo.request.*;
import com.dj.mingkong.admin.biz.controller.music.vo.response.MusicianReviewVo;
import com.dj.mingkong.admin.biz.controller.music.vo.response.MusicianVo;
import com.dj.mingkong.admin.biz.controller.system.vo.response.UserInfoVo;
import com.dj.mingkong.admin.biz.repository.database.music.*;
import com.dj.mingkong.admin.biz.repository.database.system.DictionaryRepository;
import com.dj.mingkong.admin.biz.repository.database.system.UserRepository;
import com.dj.mingkong.admin.biz.service.music.MusicianService;
import com.dj.mingkong.common.constant.CommonConstants;
import com.dj.mingkong.common.enums.MusicianStatusEnum;
import com.dj.mingkong.common.enums.StatusEnum;
import com.dj.mingkong.common.exception.ValidateException;
import com.dj.mingkong.common.infrastructure.base.dto.delete.DeleteVo;
import com.dj.mingkong.common.infrastructure.base.dto.page.PageVo;
import com.dj.mingkong.common.infrastructure.base.eo.BaseEo;
import com.dj.mingkong.common.infrastructure.eo.database.music.*;
import com.dj.mingkong.common.infrastructure.eo.database.system.UserEo;
import com.dj.mingkong.common.util.AssertUtils;
import com.dj.mingkong.common.util.SmartBeanCopy;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;


/**
 * 音乐人管理服务实现类
 *
 * @author zenglonghui
 * @date: 2022/7/22 15:53
 */
@Slf4j
@Service("musicianService")
public class MusicianServiceImpl implements MusicianService {
    @Resource
    private MusicianRepository musicianRepository;
    @Resource
    private TypeRepository typeRepository;
    @Resource
    private StyleRepository styleRepository;
    @Resource
    private UserRepository userRepository;
    @Resource
    private DictionaryRepository dictionaryRepository;
    @Resource
    private MusicianReviewRepository musicianReviewRepository;
    @Resource
    private MusicianStyleRepository musicianStyleRepository;
    @Resource
    private MusicianTypeRepository musicianTypeRepository;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void insert(MusicianAddVo addVo) {
        checkMusicianAddVo(addVo);
        final MusicianEo musicianEo = SmartBeanCopy.copy(addVo, MusicianEo.class);
        if (StringUtils.isBlank(addVo.getRealName())) {
            musicianEo.setRealName(addVo.getStageName().substring(addVo.getStageName().length() - 2));
        }
        musicianRepository.save(musicianEo);
        saveMusicianStyle(musicianEo.getId(), addVo.getStyleIdList());
        saveMusicianType(musicianEo.getId(), addVo.getTypeIdList());
        saveMusicianReviewVo(musicianEo, addVo.getMusicianReviewAddVo());
    }

    private void saveMusicianType(Long musicianId, List<Long> typeIdList) {
        final List<MusicianTypeEo> musicianTypeEos = musicianTypeRepository.listByMusicianId(musicianId);
        if (CollectionUtils.isNotEmpty(musicianTypeEos)) {
            final List<Long> addList = typeIdList.stream().filter(aLong ->
                    !musicianTypeEos.stream().map(MusicianTypeEo::getTypeId).toList().contains(aLong)).toList();
            saveMusicianType(addList, musicianId);
            final List<Long> delList = musicianTypeEos.stream().filter(typeEo ->
                    !typeIdList.contains(typeEo.getTypeId())).map(BaseEo::getId).toList();
            musicianTypeRepository.deleteByIds(delList);
        } else {
            saveMusicianType(typeIdList, musicianId);
        }
    }

    private void saveMusicianType(List<Long> typeIdList, Long musicianId) {
        if (CollectionUtils.isNotEmpty(typeIdList)) {
            final List<MusicianTypeEo> typeEos = typeIdList.stream().map(aLong ->
                    MusicianTypeEo.builder().typeId(aLong).musicianId(musicianId).build()).toList();
            musicianTypeRepository.saveBatch(typeEos);
        }
    }

    private void saveMusicianStyle(Long musicianId, List<Long> styleIdList) {
        final List<MusicianStyleEo> musicianStyleEos = musicianStyleRepository.listByMusicianId(musicianId);
        if (CollectionUtils.isNotEmpty(musicianStyleEos)) {
            final List<Long> addList = styleIdList.stream().filter(aLong ->
                    !musicianStyleEos.stream().map(MusicianStyleEo::getStyleId).toList().contains(aLong)).toList();
            saveMusicianStyle(addList, musicianId);
            final List<Long> delList = musicianStyleEos.stream().filter(styleEo ->
                    !styleIdList.contains(styleEo.getStyleId())).map(BaseEo::getId).toList();
            musicianStyleRepository.deleteByIds(delList);
        } else {
            saveMusicianStyle(styleIdList, musicianId);
        }
    }

    private void saveMusicianStyle(List<Long> styleIdList, Long musicianId) {
        if (CollectionUtils.isNotEmpty(styleIdList)) {
            final List<MusicianStyleEo> styleEos = styleIdList.stream().map(aLong ->
                    MusicianStyleEo.builder().styleId(aLong).musicianId(musicianId).build()).toList();
            musicianStyleRepository.saveBatch(styleEos);
        }
    }

    private void saveMusicianReviewVo(MusicianEo musicianEo, MusicianReviewAddVo musicianReviewAddVo) {
        final MusicianReviewEo musicianReviewEo = SmartBeanCopy.copy(musicianReviewAddVo, MusicianReviewEo.class);
        musicianReviewEo.setMusicianId(musicianEo.getId()).setStatus(musicianEo.getStatus());
        musicianReviewRepository.save(musicianReviewEo);
    }

    private void checkMusicianAddVo(MusicianAddVo musicianVo) {
        if (Objects.nonNull(musicianVo.getUserId()) && !CommonConstants.DEFAULT_ID.equals(musicianVo.getUserId())) {
            AssertUtils.isNotNull(userRepository.getById(musicianVo.getUserId()), "userId invalid");
        }

        AssertUtils.isNotNull(dictionaryRepository.oneByFullKey(musicianVo.getMusicianReviewAddVo().getMsgKey()), "msgKey invalid");

        if (musicianVo.getStyleIdList().size() != styleRepository.countByIds(musicianVo.getStyleIdList())) {
            throw new ValidateException("styleId invalid");
        }

        if (musicianVo.getTypeIdList().size() != typeRepository.countByIds(musicianVo.getTypeIdList())) {
            throw new ValidateException("typeId invalid");
        }
    }

    private void checkMusicianUpdateVo(MusicianUpdateVo musicianVo) {
        if (Objects.nonNull(musicianVo.getUserId()) && !CommonConstants.DEFAULT_ID.equals(musicianVo.getUserId())) {
            AssertUtils.isNotNull(userRepository.getById(musicianVo.getUserId()), "userId invalid");
        }

        AssertUtils.isNotNull(dictionaryRepository.oneByFullKey(musicianVo.getMusicianReviewUpdateVo().getMsgKey()), "msgKey invalid");

        if (musicianVo.getStyleIdList().size() != styleRepository.countByIds(musicianVo.getStyleIdList())) {
            throw new ValidateException("styleId invalid");
        }

        if (musicianVo.getTypeIdList().size() != typeRepository.countByIds(musicianVo.getTypeIdList())) {
            throw new ValidateException("typeId invalid");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateById(MusicianUpdateVo updateVo) {
        checkMusicianUpdateVo(updateVo);
        MusicianEo musicianEo = SmartBeanCopy.copy(updateVo, MusicianEo.class);
        if (!MusicianStatusEnum.PASS.equalsIntVal(updateVo.getStatus())) {
            musicianEo = new MusicianEo();
            musicianEo.setStatus(updateVo.getStatus()).setId(updateVo.getId());
        }
        musicianRepository.updateById(musicianEo);
        if (MusicianStatusEnum.PASS.equalsIntVal(updateVo.getStatus())) {
            saveMusicianStyle(musicianEo.getId(), updateVo.getStyleIdList());
            saveMusicianType(musicianEo.getId(), updateVo.getTypeIdList());
        }
        if (!MusicianStatusEnum.PENDING.equalsIntVal(updateVo.getStatus())
                && !MusicianStatusEnum.UNDER.equalsIntVal(updateVo.getStatus())) {
            updateMusicianReviewVo(musicianEo, updateVo.getMusicianReviewUpdateVo());
        }
    }

    private void updateMusicianReviewVo(MusicianEo musicianEo, MusicianReviewUpdateVo musicianReviewVo) {
        final MusicianReviewEo musicianReviewEo = SmartBeanCopy.copy(musicianReviewVo, MusicianReviewEo.class);
        musicianReviewEo.setMusicianId(musicianEo.getId()).setStatus(musicianEo.getStatus());
        if (Objects.isNull(musicianReviewEo.getId())) {
            musicianReviewRepository.save(musicianReviewEo);
        } else {
            musicianReviewRepository.updateById(musicianReviewEo);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteById(DeleteVo deleteVo) {
        musicianRepository.deleteById(deleteVo.getId());
        musicianReviewRepository.deleteByMusicianId(deleteVo.getId());
        musicianStyleRepository.deleteByMusicianId(deleteVo.getId());
        musicianTypeRepository.deleteByMusicianId(deleteVo.getId());
    }

    @Override
    public PageVo<MusicianVo> pageByVo(MusicianQueryVo queryVo) {
        final Page<MusicianEo> eoPage = musicianRepository.listPageByVo(queryVo);
        final PageVo<MusicianVo> musicianVoPageVo = SmartBeanCopy.copyPageVo(eoPage, MusicianVo.class);
        buildPageVo(musicianVoPageVo);
        return musicianVoPageVo;
    }

    @Override
    public List<MusicianVo> listAll() {
        final List<MusicianEo> musicianEos = musicianRepository.list();
        return SmartBeanCopy.copy(musicianEos, MusicianVo.class);
    }

    private void buildPageVo(PageVo<MusicianVo> musicianVoPageVo) {
        final List<MusicianVo> vos = musicianVoPageVo.getRecords();
        if (CollectionUtils.isNotEmpty(vos)) {
            vos.forEach(vo -> {
                final UserEo userEo = userRepository.getById(vo.getUserId());
                vo.setUserInfoVo(Objects.isNull(userEo) ? UserInfoVo.builder().build()
                        : SmartBeanCopy.copy(userEo, UserInfoVo.class));
                final List<MusicianReviewEo> musicianReviewEos = musicianReviewRepository.lambdaQuery()
                        .eq(MusicianReviewEo::getMusicianId, vo.getId()).orderByDesc(BaseEo::getCreateTime).list();
                final List<MusicianReviewVo> musicianReviewVos = SmartBeanCopy.copy(musicianReviewEos, MusicianReviewVo.class);
                final List<Long> styleIdList = getStyleIdList(vo.getId());
                final List<Long> typeIdList = getTypeIdList(vo.getId());
                vo.setMusicianReviewVoList(musicianReviewVos)
                        .setMusicianReviewVo(CollectionUtils.isNotEmpty(musicianReviewVos)
                                ? musicianReviewVos.get(0) : MusicianReviewVo.builder().build())
                        .setStyleIdList(styleIdList).setTypeIdList(typeIdList)
                        .setMusicStyleStr(buildMusicStyleStr(styleIdList)).setMusicTypeStr(buildMusicTypeStr(typeIdList));
            });
        }
    }

    private String buildMusicTypeStr(List<Long> typeIdList) {
        if (CollectionUtils.isEmpty(typeIdList)) {
            return Constants.UNKNOWN;
        }
        final List<TypeEo> typeEos = typeRepository.listByIds(typeIdList);
        return typeEos.stream().map(TypeEo::getName).distinct().collect(Collectors.joining(" | "));
    }

    private String buildMusicStyleStr(List<Long> styleIdList) {
        if (CollectionUtils.isEmpty(styleIdList)) {
            return Constants.UNKNOWN;
        }
        final List<StyleEo> styleEos = styleRepository.listByIds(styleIdList);
        return styleEos.stream().map(StyleEo::getName).distinct().collect(Collectors.joining(" | "));
    }

    private List<Long> getTypeIdList(Long musicianId) {
        final List<MusicianTypeEo> musicianTypeEos = musicianTypeRepository.lambdaQuery()
                .eq(MusicianTypeEo::getMusicianId, musicianId).list();
        return musicianTypeEos.stream().map(MusicianTypeEo::getTypeId).toList();
    }

    private List<Long> getStyleIdList(Long musicianId) {
        final List<MusicianStyleEo> musicianStyleEos = musicianStyleRepository.lambdaQuery()
                .eq(MusicianStyleEo::getMusicianId, musicianId).list();
        return musicianStyleEos.stream().map(MusicianStyleEo::getStyleId).toList();
    }
}
